package com.hyzx.qbasic.admin.domain.service.impl;

import com.hyzx.qbasic.admin.model.dto.AdminUserInfo;
import com.hyzx.qbasic.admin.model.dto.ProcessStepInfo;
import com.hyzx.qbasic.admin.model.enums.ProcessInstanceStatus;
import com.hyzx.qbasic.admin.model.enums.ProcessTaskStatus;
import com.hyzx.qbasic.admin.model.po.*;
import com.hyzx.qbasic.admin.model.vo.res.admin.GetProcessStepsRes;
import com.hyzx.qbasic.common.util.DateUtils;
import com.hyzx.qbasic.admin.domain.repository.IProcessConfigRepository;
import com.hyzx.qbasic.admin.domain.repository.IProcessDefinitionRepository;
import com.hyzx.qbasic.admin.domain.repository.IProcessInstanceRepository;
import com.hyzx.qbasic.admin.domain.service.IAdminUserService;
import com.hyzx.qbasic.admin.domain.service.IProcessFacadeService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by 黄江华 on 2018/11/29
 */

@Service
public class ProcessFacadeServiceImpl implements IProcessFacadeService {
    @Autowired
    private IProcessInstanceRepository processInstanceRepository;

    @Autowired
    private IProcessDefinitionRepository processDefinitionRepository;

    @Autowired
    private IProcessConfigRepository processConfigRepository;

    @Autowired
    private IAdminUserService userService;

    /**
     * 流程实例启动
     *
     * @param processDefinitionId 流程定义ID
     * @param businessKey         业务Key，业务数据主键,如请假申请表单数据ID
     * @param startUserId         流程实例发起人
     * @return
     */
    @Override
    public ProcessInstancePO startProcessInstanceById(int processDefinitionId,
                                                      String businessKey,
                                                      String startUserId) {
        //获取流程定义
        ProcessDefinitionPO processDefinition = processDefinitionRepository.getProcessDefinition(processDefinitionId);
        if (processDefinition == null) {
            //流程定义不存在
            return null;
        }

        ProcessInstancePO processInstance = new ProcessInstancePO();
        processInstance.setProcessDefinitionId(processDefinitionId);
        processInstance.setBusinessKey(businessKey);
        processInstance.setStartTime(new Date());
        processInstance.setStartUserId(startUserId);
        processInstance.setCurrentProcessNode((byte) 1);
        processInstance.setProcessNodes(processDefinition.getProcessNodes());
        processInstance.setProcessStatus((byte) ProcessInstanceStatus.PROCESSING.ordinal());

        ProcessTaskInstancePO firstTask = null;

        //如果流程无任务节点直接结束流程
        if (processDefinition.getProcessNodes() == 0) {
            processInstance.setEndTime(processInstance.getStartTime());
            processInstance.setDuration(0L);
            processInstance.setCurrentProcessNode((byte) 0);
            processInstance.setProcessStatus((byte) ProcessInstanceStatus.SUCCESS.ordinal());
        } else {
            List<ProcessTaskDefinitionPO> taskDefinitionList = processDefinitionRepository.getTaskDefinition(processDefinitionId);
            if (CollectionUtils.isNotEmpty(taskDefinitionList)) {
                firstTask = new ProcessTaskInstancePO();
                firstTask.setProcessDefinitionId(processDefinitionId);
                firstTask.setBusinessKey(businessKey);
                firstTask.setTaskDefinitionId(taskDefinitionList.get(0).getTaskDefinitionId());
                firstTask.setTaskName(taskDefinitionList.get(0).getTaskName());
                firstTask.setTaskNode(taskDefinitionList.get(0).getTaskNode());
                firstTask.setStartTime(new Date());
                firstTask.setTaskStatus((byte) ProcessTaskStatus.PROCESSING.ordinal());
            }
        }

        boolean success = processInstanceRepository.startProcessInstance(processInstance, firstTask);
        return success ? processInstance : null;
    }

    /**
     * 流程实例启动
     *
     * @param processConfigKey 流程配置Key：开发人员自定义的唯一的业务编码
     * @param businessKey      业务Key，业务数据主键,如请假申请表单数据ID
     * @param startUserId      流程实例发起人
     * @return
     */
    @Override
    public ProcessInstancePO startProcessInstanceByKey(String processConfigKey,
                                                       String businessKey,
                                                       String startUserId) {
        ProcessConfigPO processConfig = processConfigRepository.getProcessConfigByKey(processConfigKey);
        if (processConfig == null) {
            return null;
        }

        return startProcessInstanceById(processConfig.getProcessDefinitionId(), businessKey, startUserId);
    }

    /**
     * 任务结束
     *
     * @param taskInstanceId 任务实例ID
     * @param assignee       任务执行人
     * @param comment        备注
     * @param rejected       任务是否被驳回
     * @return
     */
    @Override
    public boolean completeTaskById(int taskInstanceId, String assignee, String comment, boolean rejected) {
        ProcessTaskInstancePO currentTask = processInstanceRepository.getProcessTaskInstance(taskInstanceId);
        return completeTask(currentTask, assignee, comment, rejected);
    }

    /**
     * 任务结束
     *
     * @param businessKey 业务Key
     * @param assignee    任务执行人
     * @param comment     备注
     * @param rejected    任务是否被驳回
     * @return
     */
    @Override
    public boolean completeTaskByBusinessKey(String businessKey, String assignee, String comment, boolean rejected) {
        List<ProcessTaskInstancePO> taskInstanceList = processInstanceRepository.getProcessTaskInstanceByBusinessKey(businessKey);
        if (CollectionUtils.isEmpty(taskInstanceList))
            return false;

        ProcessTaskInstancePO currentTask = taskInstanceList.stream()
                .filter(t -> t.getTaskStatus().byteValue() == ProcessTaskStatus.PROCESSING.ordinal())
                .max(Comparator.comparingInt(ProcessTaskInstancePO::getTaskInstanceId))
                .get();

        return completeTask(currentTask, assignee, comment, rejected);
    }

    /**
     * 任务结束
     *
     * @param currentTask 当前执行任务
     * @param assignee    任务执行人
     * @param comment     备注
     * @param rejected    任务是否被驳回
     * @return
     */
    @Override
    public boolean completeTask(ProcessTaskInstancePO currentTask, String assignee, String comment, boolean rejected) {
        if (currentTask == null) {
            return false;
        }

        //任务已结束
        if (currentTask.getTaskStatus().byteValue() != ProcessTaskStatus.PROCESSING.ordinal()) {
            return false;
        }

        List<ProcessTaskDefinitionPO> taskDefinitionList = processDefinitionRepository.getTaskDefinition(currentTask.getProcessDefinitionId());
        if (CollectionUtils.isEmpty(taskDefinitionList)) {
            return false;
        }

        List<String> candidates = taskDefinitionList.stream()
                .filter(t -> t.getTaskNode().byteValue() == currentTask.getTaskNode())
                .map(t -> t.getCandidate())
                .collect(Collectors.toList());

        //用户无权限
        if (!candidates.contains(assignee)) {
            return false;
        }

        ProcessTaskStatus taskStatus = rejected ? ProcessTaskStatus.FAILURE : ProcessTaskStatus.SUCCESS;

        currentTask.setAssignee(assignee);
        currentTask.setEndTime(new Date());
        long duration = currentTask.getEndTime().getTime() - currentTask.getStartTime().getTime();
        currentTask.setDuration(duration > 0 ? duration : 0);
        currentTask.setTaskStatus((byte) taskStatus.ordinal());
        currentTask.setComment(comment);

        //判断流程是否结束
        ProcessTaskInstancePO nextTask = null;
        ProcessInstancePO processInstance = processInstanceRepository.getProcessInstance(currentTask.getProcessInstanceId());
        if (rejected || currentTask.getTaskNode().byteValue() >= processInstance.getProcessNodes().byteValue()) {
            //流程结束
            processInstance.setEndTime(new Date());
            long processDuration = processInstance.getEndTime().getTime() - processInstance.getStartTime().getTime();
            processInstance.setDuration(processDuration > 0 ? processDuration : 0);
            processInstance.setProcessStatus((byte) taskStatus.ordinal());
        } else {
            //流程未结束
            processInstance.setCurrentProcessNode((byte) (processInstance.getCurrentProcessNode() + 1));

            ProcessTaskDefinitionPO nextTaskDefinition = taskDefinitionList.stream()
                    .filter(t -> t.getTaskNode().byteValue() == processInstance.getCurrentProcessNode().byteValue())
                    .findFirst().get();

            nextTask = new ProcessTaskInstancePO();
            nextTask.setProcessInstanceId(processInstance.getProcessInstanceId());
            nextTask.setProcessDefinitionId(processInstance.getProcessDefinitionId());
            nextTask.setBusinessKey(processInstance.getBusinessKey());
            nextTask.setTaskDefinitionId(nextTaskDefinition.getTaskDefinitionId());
            nextTask.setTaskName(nextTaskDefinition.getTaskName());
            nextTask.setTaskNode(nextTaskDefinition.getTaskNode());
            nextTask.setStartTime(new Date());
            nextTask.setTaskStatus((byte) ProcessTaskStatus.PROCESSING.ordinal());
        }

        return processInstanceRepository.completeTask(processInstance, currentTask, nextTask);
    }

    /**
     * 获取流程进度信息
     * 如果一个businessKey绑定了多个流程实例，则只返回最新的流程实例进度信息
     *
     * @param businessKey 业务Key
     * @return
     */
    @Override
    public GetProcessStepsRes getProcessSteps(String businessKey) {
        GetProcessStepsRes res = new GetProcessStepsRes();

        List<ProcessTaskInstancePO> taskInstanceList = processInstanceRepository.getProcessTaskInstanceByBusinessKey(businessKey);
        if (CollectionUtils.isEmpty(taskInstanceList)) {
            res.setSteps(Collections.emptyList());
            res.setActive(0);
            return res;
        }

        //如果一个businessKey绑定了多个流程实例，则只返回最新的流程实例进度信息
        ProcessTaskInstancePO lastTask = taskInstanceList.get(taskInstanceList.size() - 1);
        taskInstanceList = taskInstanceList.stream()
                .filter(task -> task.getProcessInstanceId().byteValue() == lastTask.getProcessInstanceId().byteValue())
                .collect(Collectors.toList());

        List<ProcessStepInfo> steps = new ArrayList<>();

        //添加已完成流程任务节点
        List<ProcessTaskInstancePO> completedTaskList = taskInstanceList.stream()
                .filter(task -> task.getTaskStatus().byteValue() != ProcessTaskStatus.PROCESSING.ordinal())
                .collect(Collectors.toList());
        for (ProcessTaskInstancePO taskInstance : completedTaskList) {
            String title = taskInstance.getTaskName();

            String description = StringUtils.isEmpty(taskInstance.getComment()) ? StringUtils.EMPTY : "：" + taskInstance.getComment();
            description = taskInstance.getTaskStatus() == 1 ? "通过" + description : "驳回" + description;

            AdminUserInfo assignee = userService.getUserInfo(Integer.parseInt(taskInstance.getAssignee()));
            ProcessStepInfo stepInfo = new ProcessStepInfo();
            stepInfo.setTitle(title);
            stepInfo.setCandidate(assignee.getRealName());
            stepInfo.setDescription(description);
            stepInfo.setDate(DateUtils.parseToString(taskInstance.getEndTime()));

            steps.add(stepInfo);
        }

        if (lastTask != null && lastTask.getTaskStatus().byteValue() != ProcessTaskStatus.PROCESSING.ordinal()) {
            //流程已结束
            res.setActive(steps.size() + 1);
        } else {//流程未结束， 添加待执行任务节点

            //获取当前流程实例所在的任务节点
            int currentProcessNode = lastTask.getTaskNode();

            //获取审核配置列表
            List<ProcessTaskDefinitionPO> taskDefinitionList = processDefinitionRepository.getTaskDefinition(lastTask.getProcessDefinitionId());
            int totalProcessNodeCount = taskDefinitionList.stream()
                    .map(d -> d.getTaskNode())
                    .max(Byte::compareTo).get();

            for (int i = currentProcessNode; i <= totalProcessNodeCount; i++) {
                final int index = i;
                String title = taskDefinitionList.stream()
                        .filter(d -> (int) (d.getTaskNode().byteValue()) == index)
                        .map(d -> d.getTaskName())
                        .findFirst().get();

                //获取候选人列表
                String auditors = taskDefinitionList.stream()
                        .filter(d -> (int) (d.getTaskNode().byteValue()) == index)
                        .map(d -> userService.getUserInfo(Integer.parseInt(d.getCandidate())))
                        .map(user -> user.getRealName())
                        .reduce((user1, user2) -> user1 + " " + user2).orElse("");

                ProcessStepInfo stepInfo = new ProcessStepInfo();
                stepInfo.setTitle(title);
                stepInfo.setCandidate(auditors);
                steps.add(stepInfo);
            }

            res.setActive(completedTaskList.size());
        }

        ProcessStepInfo lastStep = new ProcessStepInfo();
        lastStep.setTitle("完成");
        steps.add(lastStep);

        res.setSteps(steps);
        return res;
    }

    /**
     * 批量删除流程实例
     *
     * @param businessKeyList
     * @return
     */
    @Override
    public boolean batchDeleteProcessInstance(List<String> businessKeyList) {
        return processInstanceRepository.batchDeleteProcessInstance(businessKeyList);
    }

    @Override
    public ProcessInstancePO getProcessInstance(int processInstanceId) {
        return processInstanceRepository.getProcessInstance(processInstanceId);
    }

    @Override
    public ProcessInstancePO getProcessInstanceByBusinessKey(String businessKey) {
        return processInstanceRepository.getProcessInstanceByBusinessKey(businessKey);
    }
}
